package org.iabako.shared.entity.business;

import com.google.gwt.user.client.rpc.GwtTransient;
import org.iabako.shared.entity.enumeration.InstallmentsFrequency;
import org.iabako.shared.entity.enumeration.InstallmentsPeriodicalFrequency;
import org.iabako.shared.entity.enumeration.InstallmentsPeriodicalStart;
import org.iabako.shared.entity.enumeration.InstallmentsType;
import org.iabako.shared.tools.DateTools;
import org.iabako.shared.tools.GenericTools;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by jose on 14/08/14.
 */

@Entity
@Table(name = "INSTALLMENTS_DEF")
public class InstallmentsDefinition extends AbstractEntity implements Serializable {

    @Id
    @GeneratedValue
    @Column(name = "ID")
    private Long id;

    @Column(name = "TYPE")
    @Enumerated(EnumType.STRING)
    private InstallmentsType installmentsType;

    @Column(name = "NUMBER")
    private int installmentsNumber;

    @Column(name = "FREQUENCY")
    @Enumerated(EnumType.STRING)
    private InstallmentsFrequency installmentsFrequency;

    @Column(name = "PERIODICAL_FREQUENCY")
    @Enumerated(EnumType.STRING)
    private InstallmentsPeriodicalFrequency installmentsPeriodicalFrequency;

    @Column(name = "PERIODICAL_FREQUENCY_N")
    private int installmentsPeriodicalFrequencyN;

    @Column(name = "PERIODICAL_START")
    @Enumerated(EnumType.STRING)
    private InstallmentsPeriodicalStart installmentsPeriodicalStart;

    @Column(name = "PERIODICAL_START_DATE")
    @Temporal(value = TemporalType.DATE)
    private Date installmentsPeriodicalStartDate;

    @Column(name = "PERIODICAL_PRICE")
    private Double installmentsPeriodicalPrice;

    @Column(name = "NEXT_UNDEFINED_INSTALLMENT")
    @Temporal(value = TemporalType.DATE)
    private Date nextUndefinedInstallment;

    @Column(name = "LAST_UNDEFINED_INSTALLMENT")
    @Temporal(value = TemporalType.DATE)
    private Date lastUndefinedInstallment;

    @Column(name = "STOP_UNDEFINED_INSTALLMENTS")
    @Temporal(value = TemporalType.DATE)
    private Date stopUndefinedInstallments;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "installmentsDefinition")
    private Sale sale;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "installmentsDefinition")
    //To Avoid serializing exception in lazy/proxy objects.
    @GwtTransient
    private List<Installment> installments;

    //For GWT client side use to avoid timezone conversions
    @Transient
    private String installmentsPeriodicalStartDateString;

    @Transient
    private String lastUndefinedInstallmentString;

    @Transient
    private String stopUndefinedInstallmentsString;

    public InstallmentsType getInstallmentsType() {
        return installmentsType;
    }

    public void setInstallmentsType(InstallmentsType installmentsType) {
        this.installmentsType = installmentsType;
    }

    public int getInstallmentsNumber() {
        return installmentsNumber;
    }

    public void setInstallmentsNumber(int installmentsNumber) {
        this.installmentsNumber = installmentsNumber;
    }

    public InstallmentsFrequency getInstallmentsFrequency() {
        return installmentsFrequency;
    }

    public void setInstallmentsFrequency(InstallmentsFrequency installmentsFrequency) {
        this.installmentsFrequency = installmentsFrequency;
    }

    public InstallmentsPeriodicalFrequency getInstallmentsPeriodicalFrequency() {
        return installmentsPeriodicalFrequency;
    }

    public void setInstallmentsPeriodicalFrequency(InstallmentsPeriodicalFrequency installmentsPeriodicalFrequency) {
        this.installmentsPeriodicalFrequency = installmentsPeriodicalFrequency;
    }

    public int getInstallmentsPeriodicalFrequencyN() {
        return installmentsPeriodicalFrequencyN;
    }

    public void setInstallmentsPeriodicalFrequencyN(int installmentsPeriodicalFrequencyN) {
        this.installmentsPeriodicalFrequencyN = installmentsPeriodicalFrequencyN;
    }

    public InstallmentsPeriodicalStart getInstallmentsPeriodicalStart() {
        return installmentsPeriodicalStart;
    }

    public void setInstallmentsPeriodicalStart(InstallmentsPeriodicalStart installmentsPeriodicalStart) {
        this.installmentsPeriodicalStart = installmentsPeriodicalStart;
    }

    public Date getInstallmentsPeriodicalStartDate() {
        return installmentsPeriodicalStartDate;
    }

    public void setInstallmentsPeriodicalStartDate(Date installmentsPeriodicalStartDate) {
        this.installmentsPeriodicalStartDate = installmentsPeriodicalStartDate;
    }

    public String getInstallmentsPeriodicalStartDateString() {
        if (installmentsPeriodicalStartDateString == null) {
            installmentsPeriodicalStartDateString = GenericTools.formatDateToString(installmentsPeriodicalStartDate);
        }
        return installmentsPeriodicalStartDateString;
    }

    public void setInstallmentsPeriodicalStartDateString(String installmentsPeriodicalStartDateString) {
        this.installmentsPeriodicalStartDateString = installmentsPeriodicalStartDateString;
    }

    public Double getInstallmentsPeriodicalPrice() {
        return installmentsPeriodicalPrice;
    }

    public void setInstallmentsPeriodicalPrice(Double installmentsPeriodicalPrice) {
        this.installmentsPeriodicalPrice = installmentsPeriodicalPrice;
    }

    public Date getNextUndefinedInstallment() {
        return nextUndefinedInstallment;
    }

    public void setNextUndefinedInstallment(Date nextUndefinedInstallment) {
        this.nextUndefinedInstallment = nextUndefinedInstallment;
    }

    public Date getLastUndefinedInstallment() {
        return lastUndefinedInstallment;
    }

    public void setLastUndefinedInstallment(Date lastUndefinedInstallment) {
        this.lastUndefinedInstallment = lastUndefinedInstallment;
    }

    public String getLastUndefinedInstallmentString() {
        if (lastUndefinedInstallmentString == null) {
            lastUndefinedInstallmentString = GenericTools.formatDateToString(lastUndefinedInstallment);
        }
        return lastUndefinedInstallmentString;
    }

    public void setStopUndefinedInstallments(Date stopUndefinedInstallments) {
        this.stopUndefinedInstallments = stopUndefinedInstallments;
    }

    public Date getStopUndefinedInstallments() {
        return stopUndefinedInstallments;
    }

    public String getStopUndefinedInstallmentsString() {
        if (stopUndefinedInstallmentsString == null) {
            stopUndefinedInstallmentsString = GenericTools.formatDateToString(stopUndefinedInstallments);
        }
        return stopUndefinedInstallmentsString;
    }

    public Sale getSale() {
        return sale;
    }

    public void setSale(Sale sale) {
        this.sale = sale;
    }

    public List<Installment> getInstallments() {
        if (installments == null) {
            installments = new ArrayList<Installment>();
        }
        return installments;
    }

    public void setInstallments(List<Installment> installments) {
        this.installments = installments;
    }

    @Transient
    public void calculatePastFutureInstallments(DateTools dateTools, Double totalPrice, List<Installment> pastInstallment, List<Installment> futureInstallment) {
        Date now = GenericTools.getDateDay();

        if (installmentsType == InstallmentsType.defined && installmentsNumber > 0) {

            if (installmentsFrequency == InstallmentsFrequency.periodical) {

                //Installment defined and periodicals

                if (installmentsPeriodicalFrequency == null || installmentsPeriodicalStartDate == null
                        || installmentsPeriodicalFrequencyN <= 0) {
                    return;
                }

                Date installmentDate = installmentsPeriodicalStartDate;
                int installmentsCounter = 1;
                double sum = 0;
                while (installmentsCounter <= installmentsNumber) {
                    Date installmentDateToAssign = (Date) installmentDate.clone();
                    Installment installment = getNewInstallment(installmentDateToAssign, installmentsPeriodicalPrice);

                    if (totalPrice != null && installmentsCounter == installmentsNumber) {
                        //is the last one
                        installment.setAmount(totalPrice - sum);
                    } else {
                        sum += installment.getAmount();
                    }

                    if (installmentDate.compareTo(now) <= 0) {
                        pastInstallment.add(installment);
                    } else {
                        futureInstallment.add(installment);
                    }

                    installmentDate = addNToDate(dateTools, installmentDate, installmentsPeriodicalFrequency, installmentsPeriodicalFrequencyN);
                    installmentsCounter++;
                }
                return;

            } else {
                //Installment defined and customs

                for (Installment customInstallment : installments) {
                    Date installmentDate = customInstallment.getDate();
                    Double installmentPrice = customInstallment.getAmount();

                    if (installmentDate == null || installmentPrice == null) {
                        pastInstallment.clear();
                        futureInstallment.clear();
                        return;
                    }

                    if (installmentDate.compareTo(now) <= 0) {
                        pastInstallment.add(customInstallment);
                    } else {
                        futureInstallment.add(customInstallment);
                    }
                }
                return;
            }
        } else if (installmentsType == InstallmentsType.undefined) {
            //Installment undefined (so periodicals)
            if (installmentsPeriodicalFrequency == null || installmentsPeriodicalStartDate == null
                    || installmentsPeriodicalFrequencyN <= 0) {
                return;
            }

            Date installmentDate = installmentsPeriodicalStartDate;
            while (installmentDate.compareTo(now) <= 0) {
                Date installmentDateToAssign = (Date) installmentDate.clone();
                Installment installment = getNewInstallment(installmentDateToAssign, installmentsPeriodicalPrice);
                pastInstallment.add(installment);
                installmentDate = addNToDate(dateTools, installmentDate, installmentsPeriodicalFrequency, installmentsPeriodicalFrequencyN);
            }
            return;
        }
    }

    @Transient
    private Installment getNewInstallment(Date installmentDate, Double price) {
        Installment installment = new Installment();
        installment.setDate(installmentDate);
        installment.setDateString(GenericTools.formatDateToString(installmentDate));
        installment.setAmount(price);
        return installment;
    }

    @Transient
    public Date addNToDate(DateTools dateTools, Date installmentDate, InstallmentsPeriodicalFrequency periodicalFrequency, Integer periodicalFrequencyN) {
        switch (periodicalFrequency) {
            case nDays:
                return dateTools.addDaysToDate(installmentDate, periodicalFrequencyN);
            case nWeeks:
                return dateTools.addDaysToDate(installmentDate, periodicalFrequencyN * 7);
            case nMonths:
                return dateTools.addMonthsToDate(installmentDate, periodicalFrequencyN);
        }
        return installmentDate;
    }

    @Transient
    @Override
    public void copyDatesAsString() {
        getInstallmentsPeriodicalStartDateString();
        getLastUndefinedInstallmentString();
        getStopUndefinedInstallmentsString();
        return;
    }

}
